﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Exercises
{
    [TestClass]
    public class Generators
    {

        [TestMethod]
        public void CollectionToObservable()
        {
            //initialize
            var fibonacciSeq = GenerateFibonnaci(1000);
            var fibonacciSource = fibonacciSeq.ToObservable();

            //execute
            using (var fibonnacciSub = fibonacciSource.SubscribeOn(TaskPoolScheduler.Default).
                Subscribe(x => Debug.WriteLine(x),
                ex => Debug.WriteLine("Error received from source: {0}.", ex.Message),
                () => Debug.WriteLine("End of sequence.")
                ))
            {
                Thread.Sleep(2000);
            }
            //assert
        }

        [TestMethod]
        public void EmptySource()
        {
            var log = Execute(Observable.Empty<int>());
        }

        [TestMethod]
        public void Error()
        {
            var log = Execute(Observable.Throw<int>(new Exception("Oops")));
        }

        [TestMethod]
        public void Single()
        {
            var log = Execute(Observable.Return(42));
        }

        [TestMethod]
        public void Many()
        {
            var log = Execute(Observable.Range(5, 3));
        }

        [TestMethod]
        public void Generator()
        {
            var log = Execute(Observable.Generate(0,
                i => i < 5,
                i => i + 1,
                i => i * i));
        }

        [TestMethod]
        public void GeneratorWithWait()
        {
            var log = new List<string>();
            var handler = Subscribe(Observable.Generate(0,
                i => i < 5,
                i => i + 1,
                i => i * i,
                i => TimeSpan.FromSeconds(i)), log);
            Thread.Sleep(5000);
            handler.Dispose();
        }

        private static IDisposable Subscribe(IObservable<int> source, List<string> log)
        {
            return source.Subscribe(
                x => log.Add(string.Format("OnNext: {0}", x)),
                ex => log.Add(string.Format("OnError: {0}", ex.Message)),
                () => log.Add(string.Format("OnCompleted"))
                );
        }

        private static List<string> Execute(IObservable<int> observable)
        {
            var log = new List<string>();
            Subscribe(observable, log);
            return log;
        }

        private List<ulong> GenerateFibonnaci(int i)
        {
            if (i == 0)
                return new List<ulong> { 1 };
            var result = new List<ulong> { 1, 1 };
            for (var j = 2; j < i; j++)
            {
                result.Add(result[j - 1] + result[j - 2]);
            }
            return result;
        }
    }
}
